| Option name | Type | Description | 
|---|---|---|
| config | String | The relative path to the JSON config file | 
The ConfigParser class is in charge of parsing and managing the information contained within the config JSON file.
function ConfigParser(file, app, flowviz) {
    fv = flowviz;
    this.app = app;
    this.file = file;
    this.json = null;
    this.types = null;
    this.Interactions = [];
    var that = this;
    d3.json(this.file, function(error, json) {
        if(error){
            return console.log(error);
        }
        that.json = that._CompleteJson(json);
        if(fv.DebugMode) console.log(that.json);
        FlowEdge.SetArrowProps(json.display.arrowWidth, json.display.arrowHeight);
        that.edgeData = that.json.edgeData;
        that.types = GetTypes(that.json.types, that.json, null);
        that.primaryTypes = GetPrimaryTypes(that.types);
        that.leafTypes = GetLeafTypes(that.types);
        that.draggable = json.display.draggable;
        that.layout = json.display.layout;
        that.ShowDefaultMsgs = (that.json.display.messages === "default");
        GetSvgSources(that.types, function(err) {
            if(err) throw new Error('Problem loading SVG sources: ' + err);
            fv.emit('config-ready');
        });
    });
}
function GetSvgSources(types, final) {
    async.each(types, function(type, callback) {
        if(type._file !== null) {
            console.log('Loading file: ' + type._file);
            Snap.load(type._file, function(frag) {
                type._svg = frag.select('svg').outerSVG();
                callback();
            });
        } else {
            callback();
        }
    }, final);
}
ConfigParser.prototype.GetEdgeDataObject = function(start, end) {
    var ret = {};
    _.forEach(this.edgeData, function(item, key) {
        if(IsTypeInList(item.start, start.type.type)) {
            if(IsTypeInList(item.end, end.type.type)) {
                ret[key] = $.extend(true, {}, item, null);
                return false;
            }
        }
    });
    return ret;
};
function IsTypeInList(list, type) {
    var ret = false;
    _.forEach(list, function(item) {
        if(_.endsWith(item, '.*')) {
            var trimmed = _.trimRight(item, '.*');
            ret = _.startsWith(type, trimmed);
        } else if(_.endsWith(item, '*')) {
            ret = true;
            return false;
        } else {
            ret = _.startsWith(type, item);
        }
        if(ret) return false;
    });
    return ret;
}
| Option name | Type | Description | 
|---|---|---|
| json | object | JSON object | 
| key | string | JSON key to check | 
| defaultValue | object | The default value to set undefined values to | 
Checks if a property of a json object is defined and if not, defines it and sets it to it's default value.
ConfigParser.prototype._CheckAndDefault = function(json, key, defaultValue) {
    if(!json.hasOwnProperty(key)) {
        json[key] = defaultValue;
        return false;
    }
    return true;
};
| Option name | Type | Description | 
|---|---|---|
| json | object | JSON object | 
| key | string | Key to check for | 
Checks if a JSON object has a given key. Throws an error if the JSON object is missing the key.
ConfigParser.prototype._CheckRequired = function(json, key) {
    if(!json.hasOwnProperty(key)) {
        var msg = "You must define the key \"" + key + "\" in your config file!";
        fv.emit('config-error', msg);
        throw new Error(msg);
    }
};
| Option name | Type | Description | 
|---|---|---|
| json | object | The config JSON object | 
Completes an incomplete JSON config object by cascading settings from top level type declarations and by setting
default values for undefined settings.
ConfigParser.prototype._CompleteJson = function(json) {
    // "interactions" defaults to null
    this._CheckAndDefault(json, "interactions", {});
    // "display" defaults to null
    this._CheckAndDefault(json, "display", {});
    this._CheckAndDefault(json.display, "messages", "default");
    this._CheckAndDefault(json.display, "draggable", true);
    this._CheckAndDefault(json.display, "layout", "vertical");
    this._CheckAndDefault(json.display, "scale", 1.0);
    this._CheckAndDefault(json.display, "arrowHeight", 15);
    this._CheckAndDefault(json.display, "arrowWidth", 6);
    var that = this;
    this._CheckAndDefault(json, "interactions", null);
    _.forEach(json.interactions, function(intList, key) {
        if(_.includes(Interactions.Types, key)) {
            _.forEach(intList, function(int) {
                if (int.event !== "" && int.function !== "") {
                    if (that.app.Callbacks.Interactions.hasOwnProperty(int.function)) {
                        that.Interactions.push({
                            name: key,
                            event: int.event,
                            func: that.app.Callbacks.Interactions[int.function]
                        });
                    } else {
                        throw new Error("Function " + int.function + " need to be defined under 'Interactions'!");
                    }
                } else {
                    throw new Error("You must provide both an event name and a function name for interaction " + key + "!");
                }
            });
        } else {
            throw new Error("The is no interaction of the type " + key + "!");
        }
    });
    // "edgeData" defaults to null
    this._CheckAndDefault(json, "edgeData", null);
    // "types" is required
    this._CheckRequired(json, "types");
    //var that = this;
    _.forEach(json.types, function(type, index, list) {
        that._CheckType(type, null);
    });
    //_.forEach(json.types, function(type, index, list) {
    //    that._ExpandConstraints(type);
    //});
    return json;
};
| Option name | Type | Description | 
|---|---|---|
| type | object | JSON declaration of a type | 
| parent | object | JSON declaration of this type's parent | 
Completes a type declaration based on default values and the values of the type's parent's settings
ConfigParser.prototype._CheckType = function(type, parent) {
    // types must have a name
    this._CheckRequired(type, "type");
    if(parent === null) {
        // Provide smart defaults if the parents are missing any settings
        this._CheckAndDefault(type, "name", type.type);
        this._CheckAndDefault(type, "desc", "No description provided in config file!");
        this._CheckAndDefault(type, "view", null);
        this._CheckAndDefault(type, "width", 0);
        this._CheckAndDefault(type, "height", 0);
        this._CheckAndDefault(type, "clickItems", null);
        this._CheckAndDefault(type, "callbacks", null);
        this._CheckAndDefault(type, "padding", 0);
        this._CheckAndDefault(type, "connections", "default");
        this._CheckAndDefault(type, "constraints", {
            "incoming": {
                "range": [0,99],
                "types": {
                    "*": [0,99]
                }
            },
            "outgoing": {
                "range": [0,99],
                "types": {
                    "*": [0,99]
                }
            }
        });
        this._CheckAndDefault(type.constraints, "incoming", {
            "range": [0,99],
            "types": {
                "*": [0,99]
            }
        });
        this._CheckAndDefault(type.constraints, "outgoing", {
            "range": [0,99],
            "types": {
                "*": [0,99]
            }
        });
        this._CheckAndDefault(type.constraints.incoming, "range", [0,99]);
        this._CheckAndDefault(type.constraints.incoming, "types", {"*" : type.constraints.incoming.range});
        this._CheckAndDefault(type.constraints.outgoing, "range", [0,99]);
        this._CheckAndDefault(type.constraints.outgoing, "types", {"*" : type.constraints.outgoing.range});
        this._CheckAndDefault(type, "nodeData", null);
        this._CheckAndDefault(type, "subtypes", null);
    } else {
        // Chain type names with parent't type for making it easier to expand type constraints
        var tmp = type.type;
        type.type = parent.type + "." + type.type;
        // Cascade parent's settings to the children if the children are missing any settings
        this._CheckAndDefault(type, "name", tmp);
        this._CheckAndDefault(type, "desc", parent.desc);
        this._CheckAndDefault(type, "view", parent.view);
        this._CheckAndDefault(type, "width", parent.width);
        this._CheckAndDefault(type, "height", parent.height);
        this._CheckAndDefault(type, "clickItems", parent.clickItems);
        this._CheckAndDefault(type, "callbacks", parent.callbacks);
        this._CheckAndDefault(type, "padding", parent.padding);
        this._CheckAndDefault(type, "connections", parent.connections);
        this._CheckAndDefault(type, "constraints", parent.constraints);
        this._CheckAndDefault(type.constraints, "incoming", parent.constraints.incoming);
        this._CheckAndDefault(type.constraints, "outgoing", parent.constraints.outgoing);
        this._CheckAndDefault(type.constraints.incoming, "range", parent.constraints.incoming.range);
        this._CheckAndDefault(type.constraints.incoming, "types", parent.constraints.incoming.types);
        this._CheckAndDefault(type.constraints.outgoing, "range", parent.constraints.outgoing.range);
        this._CheckAndDefault(type.constraints.outgoing, "types", parent.constraints.outgoing.types);
        this._CheckAndDefault(type, "nodeData", parent.nodeData);
        this._CheckAndDefault(type, "subtypes", null);
    }
    if(type.hasOwnProperty("subtypes") && type.subtypes !== null) {
        var that = this;
        _.forEach(type.subtypes, function(subtype, index, list) {
            that._CheckType(subtype, type);
        });
    } else {
        // Ensure all leaf types have a view
        if(type.view === null) {
            throw new Error(type.type + " must have a view in the config file!");
        }
        // Ensure all leaf types have a non-zero width and height
        if(type.width <= 0) {
            throw new Error(type.type + " must have a non-zero width in the config file!");
        }
        if(type.height <= 0) {
            throw new Error(type.type + " must have a non-zero height in the config file!");
        }
    }
};
| Option name | Type | Description | 
|---|---|---|
| types | object | A reference to the current level in the types hierarchy of the config JSON file | 
Gets an array of NodeType objects for the types at this level of the hierarchy in the config JSON file
function GetTypes(types, json, parent) {
    var output = [];
    if(types != null) {
        _.forEach(types, function(type) {
            var newType = new NodeType(
                type.type,
                type.name,
                type.desc,
                type.view
            );
            newType.SetWidth(type.width);
            newType.SetHeight(type.height);
            newType.SetScale(json.display.scale);
            newType.SetProperties(type.nodeData);
            newType.SetParent(parent);
            newType.SetPadding(type.padding);
            newType.SetConnections(type.connections);
            newType.SetConstraints(type.constraints);
            output.push(newType);
            if (type.hasOwnProperty('subtypes') && type.subtypes !== null) {
                var children = GetTypes(type.subtypes, json, newType);
                newType.SetChildren(children);
                output = output.concat(children);
            }
        });
    }
    return output;
}
| Option name | Type | Description | 
|---|---|---|
| all | Array | List of all types in the config file | 
Returns a list of all of the primary (top-level) types in the type hierarchy.
function GetPrimaryTypes(all) {
    var output = [];
    if(all !== null) {
        _.forEach(all, function(type) {
            if(!type.HasParent()) {
                output.push(type);
            }
        });
    }
    return output;
}
| Option name | Type | Description | 
|---|---|---|
| all | Array | List of all types in the config file | 
Returns a list of all of the leaf (bottom-level) types in the type hierarchy.
function GetLeafTypes(all) {
    var output = [];
    if(all !== null) {
        _.forEach(all, function(type) {
            if(!type.HasChildren()) {
                output.push(type);
            }
        });
    }
    return output;
}
Gets a list of all node types. Only a single copy of the list of NodeType objects is ever created. This function
just returns a reference to this single list. Note: this can only be called after the 'config-ready' event has
fired.
ConfigParser.prototype.getAllNodeTypes = function() {
    if(this.types !== null) {
        return this.types;
    }
    return null;
};
Gets only the top level nodes.
ConfigParser.prototype.getPrimaryNodesTypes = function() {
    if(this.primaryTypes !== null) {
        return this.primaryTypes;
    }
    return null;
};
Gets only the bottom level leaf nodes.
ConfigParser.prototype.getLeafNodesTypes = function() {
    if(this.leafTypes !== null) {
        return this.leafTypes;
    }
    return null;
};